package cn.wenxin_zhang.smart4j.plus.utils;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.helper.ConfigHelper;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Created by Wenxin on 2017/3/5.
 */
public class DBUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(DBUtils.class);

    private static final BasicDataSource DATA_SOURCE;

    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<Connection>() {
        @Override
        protected Connection initialValue() {
            return null;
        }
    };

    //获取数据源
    private static BasicDataSource getDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(ConfigHelper.getJdbcDriver());
        dataSource.setUrl(ConfigHelper.getJdbcUrl());
        dataSource.setUsername(ConfigHelper.getJdbcUsername());
        dataSource.setPassword(ConfigHelper.getJdbcPassword());
        return dataSource;
    }


    static {
        DATA_SOURCE = getDataSource();
    }

    /**
     * 一条线程分配一个连接,获得当前线程线程连接
     * 所获取连接默认为true
     * @return
     */
    public static Connection getConnection() {
        try {
            Connection connection = THREAD_LOCAL.get();
            if (connection == null) {
                Connection conn = DATA_SOURCE.getConnection();
                THREAD_LOCAL.set(conn);
                return conn;
            }
            return connection;
        } catch (Exception e) {
            LOGGER.error("get connection failure: ", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭连接
     * 关闭连接后，连接将从释放回连接池，当前线程也不再持有此连接
     * @param conn
     */
    public static void clossConnection(Connection conn) {
        if (conn != null) {
            try {
                THREAD_LOCAL.remove();
                conn.close();
            } catch (Exception e) {
                LOGGER.error("close connection failure: ", e);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 填充Statement
     *
     * @param statement
     * @param params
     */
    private static void fillState(PreparedStatement statement, Object[] params) {
        for (int i = 0; i < params.length; i++) {
            try {
                statement.setObject(i + 1, params[i]);
            } catch (Exception e) {
                LOGGER.error("fill statement failure: ", e);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 填充屁批量Statement
     *
     * @param statement
     * @param params
     */
    private static void fillStateBatch(PreparedStatement statement, Object[][] params) {
        for (int i = 0; i < params.length; i++) {
            for (int j = 0; i < params[i].length; j++)
                try {
                    statement.setObject(i * params.length + j + 1, params[i]);
                } catch (Exception e) {
                    LOGGER.error("fill statement failure: ", e);
                    throw new RuntimeException(e);
                }
        }
    }

    /**
     * 关闭Statement
     *
     * @param statement
     */
    private static void closeStatement(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (Exception e) {
                LOGGER.error("close statement failure: ", e);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 插入
     * 此方法不提交操作也不关闭连接
     *
     * @param conn
     * @param sql
     * @param params
     * @return
     */
    public static int insert(Connection conn, String sql, Object[] params) {
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(sql);
            fillState(statement, params);
            int i = statement.executeUpdate();
            return i;
        } catch (Exception e) {
            LOGGER.error("update failure: ", e);
            throw new RuntimeException(e);
        } finally {
            closeStatement(statement);
        }
    }

    /**
     * 批量插入
     * 此方法不提交操作也不关闭连接
     *
     * @param conn
     * @param sql
     * @param params
     * @return
     */
    public static int insertBatch(Connection conn, String sql, Object[][] params) {
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(sql);
            fillStateBatch(statement, params);
            int i = statement.executeUpdate();
            return i;
        } catch (Exception e) {
            LOGGER.error("update failure: ", e);
            throw new RuntimeException(e);
        } finally {
            closeStatement(statement);
        }
    }

    /**
     * 删除
     * 此方法不提交操作也不关闭连接
     *
     * @param conn
     * @param sql
     * @param params
     * @return
     */
    public static int delete(Connection conn, String sql, Object[] params) {
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(sql);
            fillState(statement, params);
            int i = statement.executeUpdate();
            return i;
        } catch (Exception e) {
            LOGGER.error("update failure: ", e);
            throw new RuntimeException(e);
        } finally {
            closeStatement(statement);
        }
    }

    /**
     * 修改
     * 此方法不提交操作也不关闭连接
     *
     * @param conn
     * @param sql
     * @param params
     * @return
     */
    public static int update(Connection conn, String sql, Object[] params) {
        try {
            QueryRunner qr = new QueryRunner();
            int i = qr.update(conn, sql, params);
            return i;
        } catch (Exception e) {
            LOGGER.error("update failure: ", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 封装了DbUtils库，返回ResultSet处理器处理结果
     * 此方法不提交操作也不关闭连接
     *
     * @param conn
     * @param sql
     * @param rsh
     * @param params
     * @param <T>
     * @return
     */
    public static <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object[] params) {
        try {
            QueryRunner qr = new QueryRunner();
            T query = qr.query(conn, sql, rsh, params);
            return query;
        } catch (Exception e) {
            LOGGER.error("query failure: ", e);
            throw new RuntimeException(e);
        }
    }

}
